/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief Implementation for fastProtocol common parts.
 * \author Antti Maula <antti.maula@tkk.fi>
 */
#include "fastProtocol_util.h"
#include "fastProtocol.h"
#include "fastProtocolExplicitMulticast.h"
#include "owndebug.h"
#include "ownutils.h"
#include "../connectionList.h"
#include "../connectionListEntry.h"
#include <assert.h>

//*****************************************************************************
//*****************************************************************************

EProtocolExit handleConnectionListReply(TFastProtocol *p,
                                        TConnectionList *l, 
                                        TConnectionListEntry *e,
                                        const TFastProtocolHeader *fastheader,
                                        const int retval)
{
  TFastProtocolHeader replyhdr;
  int sendReply = 0;
  int r = protocolExitError;

  switch(retval) {
  case CONNECTIONLIST_OK:
    r = protocolExitNoError;
    break;

  case CONNECTIONLIST_ERROR:
    dPrint(4,"Warning: connectionListSendDataByID returned error!");
    r = protocolExitNoError;
    fastProtocolConstructHeader(&replyhdr,
				fastheader->source_id,
				fastheader->target_id,
				0, 
				(FASTPROTOCOL_FLAG_ERROR),
				FASTPROTOCOL_DEFAULT_TTL,
				0x00);
    sendReply = fastheader->flags & FASTPROTOCOL_FLAG_REPORT_ERROR;
    break;

  case CONNECTIONLIST_LINKERROR:
    dPrint(4,"Warning: connectionListSendDataByID returned linkerror!");
    // Link error!
    r = protocolExitNoError;
    fastProtocolConstructHeader(&replyhdr,
				fastheader->source_id,
				fastheader->target_id,
				0, 
				(FASTPROTOCOL_FLAG_ERROR |
				 FASTPROTOCOL_FLAG_ERROR_LINK),
				FASTPROTOCOL_DEFAULT_TTL,
				0x00);
    sendReply = fastheader->flags & FASTPROTOCOL_FLAG_REPORT_ERROR;
    break;

  case CONNECTIONLIST_TARGETNOTFOUND:
    dPrint(4,"Warning: connectionListSendDataByID returned TARGETNOTFOUND error!");
    
    // Target not found!
    r = protocolExitNoError;
    fastProtocolConstructHeader(&replyhdr,
				fastheader->source_id,
				fastheader->target_id,
				0, 
				(FASTPROTOCOL_FLAG_ERROR |
				 FASTPROTOCOL_FLAG_ERROR_UNREACHABLE),
				FASTPROTOCOL_DEFAULT_TTL,
				0x00);

    sendReply = fastheader->flags & FASTPROTOCOL_FLAG_REPORT_ERROR;
    break;

  case CONNECTIONLIST_SEND_DELAYED:
    dPrint(4,"Warning: connectionListSendDataByID returned DELAYED error!");
    
    // Congestion warning?
    r = protocolExitNoError;
    fastProtocolConstructHeader(&replyhdr,
				fastheader->source_id,
				fastheader->target_id,
				0, 
				(FASTPROTOCOL_FLAG_STATUS | 
				 FASTPROTOCOL_FLAG_STATUS_CONGESTION),
				FASTPROTOCOL_DEFAULT_TTL,
				0x00); //FIXME: Maybe HUBID would be informational?
    sendReply = fastheader->flags & FASTPROTOCOL_FLAG_REPORT_STATUS;
    break;
  }

  if (sendReply) {
    dPrint(4,"After error, we will send a reply to originating client!");
    connectionListSendRawDataByEntry(l, 
				     (char *)&replyhdr, 
				     sizeof(TFastProtocolHeader),
				     e);
  }

  return r;
}
//*****************************************************************************

void fastProtocolConstructHeader(TFastProtocolHeader *hdr,
				 unsigned int target_id,
				 unsigned int source_id,
				 int payload_length,
				 int flags,
				 int ttl,
				 int statusvalue)
{
  assert(hdr != NULL);

  // Construct the ID
  protocolIdentificationConstructHeader(&hdr->protocol_id,
					FASTPROTOCOL_NUMBER,
					FASTPROTOCOL_PAYLOAD2TOTAL(payload_length));
  
  // Copy parameters
  hdr->flags = flags;
  hdr->ttl = ttl ? ttl : FASTPROTOCOL_DEFAULT_TTL;
  hdr->statusvalue = statusvalue;
  hdr->target_id = target_id;
  hdr->source_id = source_id;
}
//*************************************************************************

void fastProtocolDumpPacketHeader(int l, const TFastProtocolHeader *fastheader)
{
  // Dump the protocol generic header
  protocolIdentificationDumpHeader(l, &fastheader->protocol_id);

  if ( fastheader->protocol_id.protocol_number != FASTPROTOCOL_NUMBER ) 
    dPrint(1,"WARNING: Received packet doesn't appear to be FASTPROTOCOL packet (should be 0x%02x)",
	   FASTPROTOCOL_NUMBER);

  dumpPacketHeader(l, fastheader);
}
//*************************************************************************

const char *flagsToString(unsigned short int flags)
{
  static char flagString[256]; // BIG ENOUGH =D
  
  flagString[0] = 0;
  if (flags & FASTPROTOCOL_FLAG_REPORT_ERROR) { 
    if (flagString[0]) strcat(flagString, ",");
    strcat(flagString, "REPORT_ERROR");
  }  if (flags & FASTPROTOCOL_FLAG_REPORT_STATUS) { 
    if (flagString[0]) strcat(flagString, ",");
    strcat(flagString, "REPORT_STATUS");
  }

  if (flags & FASTPROTOCOL_FLAG_ERROR) { 
    if (flagString[0]) strcat(flagString, ",");
    strcat(flagString, "ERROR");
  }
  if (flags & FASTPROTOCOL_FLAG_ERROR_UNREACHABLE) { 
    if (flagString[0]) strcat(flagString, ",");
    strcat(flagString, "ERROR_UNREACHABLE");
  }
  if (flags & FASTPROTOCOL_FLAG_ERROR_LINK) { 
    if (flagString[0]) strcat(flagString, ",");
    strcat(flagString, "ERROR_LINK");
  }

  if (flags & FASTPROTOCOL_FLAG_STATUS) {
    if (flagString[0]) strcat(flagString, ",");
    strcat(flagString, "STATUS");
  }
  if (flags & FASTPROTOCOL_FLAG_STATUS_CONGESTION) { 
    if (flagString[0]) strcat(flagString, ",");
    strcat(flagString, "STATUS_CONGESTION");
  }

  if (flags & FASTPROTOCOL_FLAG_MODE_EXPLICIT_MULTICAST) { 
    if (flagString[0]) strcat(flagString, ",");
    strcat(flagString, "MODE_EXPLICIT_MULTICAST");
  }

  return flagString;
}
//*****************************************************************************

void dumpPacketHeader(int l, const TFastProtocolHeader *fastheader)
{
  dPrint(l,
         "FastProtocol packet:\r\n"
         "\tflags:\t%04x (%s)\r\n"
         "\tSource:\t%08x\r\n"
         "\tTarget:\t%08x\r\n"
         "\tPayload length:\t%lu",
         fastheader->flags, flagsToString(fastheader->flags),
         fastheader->source_id,
         fastheader->target_id,
         FASTPROTOCOL_TOTAL2PAYLOAD(fastheader->protocol_id.total_size));
  
  if (fastheader->flags & FASTPROTOCOL_FLAG_MODE_EXPLICIT_MULTICAST) { 
    dPrint(l,"Explicit multicast mode selected, Multicast targets are:");
    unsigned int *targetIdArrayPtr = (unsigned int *)(fastheader+1);
    int i;
    for(i=0; i < fastheader->target_id; ++i) {
      dPrint(l,"0x%08x", *targetIdArrayPtr);
      ++targetIdArrayPtr;
    }
  }  
  
}
//*****************************************************************************
//*****************************************************************************
